﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.VisualStudio.TeamFoundation;
using Microsoft.VisualStudio.TeamFoundation.WorkItemTracking;
using Microsoft.Win32;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;

namespace WorkItemCollaborator
{
    /// <summary>
    /// This is the class that implements the package exposed by this assembly.
    ///
    /// The minimum requirement for a class to be considered a valid package for Visual Studio
    /// is to implement the IVsPackage interface and register itself with the shell.
    /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
    /// to do it: it derives from the Package class that provides the implementation of the 
    /// IVsPackage interface and uses the registration attributes defined in the framework to 
    /// register itself and its components with the shell.
    /// </summary>
    // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
    // a package.
    [PackageRegistration(UseManagedResourcesOnly = true)]
    // This attribute is used to register the informations needed to show the this package
    // in the Help/About dialog of Visual Studio.
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource("Menus.ctmenu", 1)]
    // This attribute registers a tool window exposed by this package.
    [ProvideToolWindow(typeof(WICToolWindow))]
    [Guid(GuidList.guidWorkItemCollaboratorPkgString)]
    [ProvideBindingPathAttribute]
    public sealed class WorkItemCollaboratorPackage : Package
    {
        private object _lockToken = new object();
        private WICToolWindow currentWindow = null;
        Dictionary<string,IResultsDocument> openResultsDocument = new Dictionary<string, IResultsDocument>();
        WicUserStore wicUserStore = new WicUserStore();
        /// <summary>
        /// Default constructor of the package.
        /// Inside this method you can place any initialization code that does not require 
        /// any Visual Studio service because at this point the package object is created but 
        /// not sited yet inside Visual Studio environment. The place to do all the other 
        /// initialization is the Initialize method.
        /// </summary>
        public WorkItemCollaboratorPackage()
        {
            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
            wicUserStore.ProjectUsersLoaded += new ProjectUsersLoadedHandler(ProjectUsersLoaded);
        }

        /// <summary>
        /// This function is called when the user clicks the menu item that shows the 
        /// tool window. See the Initialize method to see how the menu item is associated to 
        /// this function using the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void ShowToolWindow(object sender, EventArgs e)
        {
            // Get the instance number 0 of this tool window. This window is single instance so this instance
            // is actually the only one.
            // The last flag is set to true so that if the tool window does not exists it will be created.
            ShowWorkItemCollaboratorToolWindow();
        }

        private void ShowWorkItemCollaboratorToolWindow()
        {
            CheckAndInitCurrentWindow();
            if ((null == currentWindow) || (null == currentWindow.Frame))
            {
                throw new NotSupportedException(Resources.CanNotCreateWindow);
            }
            IVsWindowFrame windowFrame = (IVsWindowFrame)currentWindow.Frame;
            Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show());
            
        }

        private void CheckAndInitCurrentWindow()
        {
            if (currentWindow == null)
            {
                currentWindow = this.FindToolWindow(typeof (WICToolWindow), 0, true) as WICToolWindow;
            }
        }


        public EnvDTE.DTE DTE
        {
            get
            {
                return this.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
            }
        }

        public IVsTeamExplorer TeamExplorer
        {
            get
            {
                return this.GetService(typeof(IVsTeamExplorer)) as IVsTeamExplorer;
            }
        }

        public DocumentService DocService
        {
            get
            {
                return DTE.GetObject("Microsoft.VisualStudio.TeamFoundation.WorkItemTracking.DocumentService")
                        as DocumentService;
            }
        }

        /////////////////////////////////////////////////////////////////////////////
        // Overriden Package Implementation
        #region Package Members

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initilaization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
            base.Initialize();

            // Add our command handlers for menu (commands must exist in the .vsct file)
            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if ( null != mcs )
            {
                // Create the command for the tool window
                CommandID toolwndCommandID = new CommandID(GuidList.guidWorkItemCollaboratorCmdSet, (int)PkgCmdIDList.cmdidWorkItemCollaborator);
                MenuCommand menuToolWin = new MenuCommand(ShowToolWindow, toolwndCommandID);
                mcs.AddCommand( menuToolWin );
            }
            // There is an active document already opened
            if(DTE.ActiveDocument != null)
            {
                ProcessDTEDocument(DTE.ActiveDocument.FullName);
            }
            else
            {
                //Check if already connected to any TFS project 
                ProjectContext selectedProjectContext = TeamExplorer.GetProjectContext();
                if (selectedProjectContext == null || String.IsNullOrEmpty(selectedProjectContext.ProjectName))
                {
                    //Team explorer is not already loading any project
                    if (TeamExplorer.ConnectionState == TeamExplorerConnectionState.Idle)
                    {
                        //This will show the popup to connect to TFS
                        this.DTE.ExecuteCommand("Team.ConnecttoTeamFoundationServer");
                        //Get the project context again
                        selectedProjectContext = TeamExplorer.GetProjectContext();
                    }
                }
                
                if (selectedProjectContext != null && !String.IsNullOrEmpty(selectedProjectContext.ProjectName))
                {
                    //Try to load project users async
                    wicUserStore.ProcessProjectUsers(selectedProjectContext);
                }
            }
            DTE.Events.WindowEvents.WindowActivated += new EnvDTE._dispWindowEvents_WindowActivatedEventHandler(WindowEvents_WindowActivated);
            
        }

        void WindowEvents_WindowActivated(EnvDTE.Window GotFocus, EnvDTE.Window LostFocus)
        {
            if (GotFocus.Document == null)
            {
                return;
            }
            string documentName = GotFocus.Document.FullName;

            ProcessDTEDocument(documentName);
        }

        private void ProcessDTEDocument(string documentName)
        {
            IWorkItemTrackingDocument workItemTrackingDocument = DocService.FindDocument(documentName, _lockToken);

            if (workItemTrackingDocument == null)
            {
                CheckAndInitCurrentWindow();
                currentWindow.CleanToolWindow();
                return;
            }
            workItemTrackingDocument.Release(_lockToken);
            ProcessCurrentWorkITemTrackingDocument(workItemTrackingDocument);
        }

        private void ProcessCurrentWorkITemTrackingDocument(IWorkItemTrackingDocument workItemTrackingDocument)
        {
            ProjectContext projectContext = TeamExplorer.GetProjectContext();
            wicUserStore.ProcessProjectUsers(projectContext);
            var resultsDocument = workItemTrackingDocument as IResultsDocument;
            if (resultsDocument != null && resultsDocument.QueryDocument != null)
            {
                if (resultsDocument.SelectedItemIds != null && resultsDocument.SelectedItemIds.Length > 0 && resultsDocument.ResultListDataProvider != null && resultsDocument.ResultListDataProvider.DisplayRowCount > 0)
                {
                    ShowSelectedWorkItemInResultDocument(resultsDocument);
                }
                if (!openResultsDocument.ContainsKey(workItemTrackingDocument.CanonicalId))
                {
                    openResultsDocument.Add(workItemTrackingDocument.CanonicalId, resultsDocument);
                    resultsDocument.SelectionChanged +=
                        new WorkItemTrackingDocumentEventHandler(ResultsDocumentSelectionChanged);
                    resultsDocument.Closed += new WorkItemTrackingDocumentEventHandler(ResultsDocumentClosed);
                }
            }
            else
            {
                IWorkItemDocument workItemDocument = workItemTrackingDocument as IWorkItemDocument;
                if (workItemDocument != null && workItemDocument.Item != null)
                {
                    WorkItem item = workItemDocument.Item;
                    if (item.Id > 0)
                    {
                        ShowWorkITem(item);
                    }
                    else
                    {
                        CheckAndInitCurrentWindow();
                        currentWindow.CleanToolWindow();
                    }
                }
            }
        }

        void ProjectUsersLoaded(string projectName)
        {
            if(currentWindow == null)
                return;
            ProjectContext projectContext = TeamExplorer.GetProjectContext();
            if (projectContext != null && projectContext.ProjectName == projectName)
            {
                Collection<User> projectUsersCollection = wicUserStore.GetProjectUsersCollection(projectContext);
                currentWindow.RefreshUsersOfCurrentlyDisplayedTaks(projectUsersCollection);
            }
        }
        private void ShowWorkITem(WorkItem item)
        {
            ProjectContext projectContext = TeamExplorer.GetProjectContext();
            Collection<User> projectUsersCollection = wicUserStore.GetProjectUsersCollection(projectContext);
            string workItemEditorUrl = GetWorkItemEditorUrl(item);
            CheckAndInitCurrentWindow();
            currentWindow.SetWorkItem(item, projectUsersCollection, workItemEditorUrl);
        }

        void ResultsDocumentClosed(object sender, EventArgs e)
        {
            IResultsDocument resultsDocument = sender as IResultsDocument;
            if (resultsDocument != null && openResultsDocument.ContainsKey(resultsDocument.CanonicalId))
            {
                openResultsDocument.Remove(resultsDocument.CanonicalId);
            }
        }

        void ResultsDocumentSelectionChanged(object sender, EventArgs e)
        {
            IResultsDocument resultsDocument = sender as IResultsDocument;
            
            if (resultsDocument != null && resultsDocument.ResultListDataProvider != null && resultsDocument.ResultListDataProvider.DisplayRowCount > 0)
            {
                if(resultsDocument.SelectedItemIds != null && resultsDocument.SelectedItemIds.Length > 0)
                {
                    ShowSelectedWorkItemInResultDocument(resultsDocument);
                }
            }
        }

        private void ShowSelectedWorkItemInResultDocument(IResultsDocument resultsDocument)
        {
            // Same workitem is being displayed
            if (currentWindow != null && currentWindow.CurrentlyDisplayedWorkItemId.HasValue && resultsDocument.SelectedItemIds[0] == currentWindow.CurrentlyDisplayedWorkItemId.Value)
                return;
            
            int index = resultsDocument.ResultListDataProvider.GetItemIndex(resultsDocument.SelectedItemIds[0]);
            WorkItem item = resultsDocument.ResultListDataProvider.GetItem(index);
            if(item != null)
            {
                ProjectContext projectContext = TeamExplorer.GetProjectContext();
                Collection<User> projectUsersCollection = wicUserStore.GetProjectUsersCollection(projectContext);
                string workItemEditorUrl = GetWorkItemEditorUrl(item);
                CheckAndInitCurrentWindow();
                currentWindow.SetWorkItem(item, projectUsersCollection, workItemEditorUrl);
            }
        }

        private string GetWorkItemEditorUrl(WorkItem item)
        {
            TfsTeamProjectCollection tpc =
                TfsTeamProjectCollectionFactory.GetTeamProjectCollection(
                    new Uri(TeamExplorer.GetProjectContext().DomainUri));

            var urlService = tpc.GetService<TswaClientHyperlinkService>();
            if (urlService != null)
            {
                Uri wiEditorUrl = urlService.GetWorkItemEditorUrl(item.Id);
                if (wiEditorUrl != null)
                    return wiEditorUrl.AbsoluteUri;
            }

            return string.Empty;
        }

        #endregion

    }
}
